# -!-coding:utf-8-!-
# @Time: 2022/5/10
# @Dev: TJ

import os
import random
import sys
import time
from requests import Session, Response
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry
from loguru import logger
from werkzeug.utils import import_string
from requests.packages.urllib3.util.ssl_ import create_urllib3_context

# 监听结果的hook
hook_session = None
monitor_path = os.path.join(os.path.split(__file__)[0], 'monitor.py')
if os.path.exists(monitor_path):
    sys.path.append(os.path.abspath(os.path.split(__file__)[0]))
    monitor = import_string('monitor')
    hook_session = monitor.Monitor.session


class ProxyChannel:
    __user = 'user'
    __pw = 'password'
    __tx_cn = 'host'
    __tx_hk = 'host'

    CLOSE = ''  # 关闭代理

    """ 国内 """
    TX_CN7001 = f'http://{__user}:{__pw}@{__tx_cn}:7001'
    TX_CN7002 = f'http://{__user}:{__pw}@{__tx_cn}:7002'
    TX_CN7003 = f'http://{__user}:{__pw}@{__tx_cn}:7003'
    TX_CN7004 = f'http://{__user}:{__pw}@{__tx_cn}:7004'
    TX_CN7005 = f'socks5://{__user}:{__pw}@{__tx_cn}:7005'
    TX_CN7009 = 'http://10.0.3.43:7009'

    """ 海外 """
    TX_HK7001 = f'http://{__user}:{__pw}@{__tx_hk}:7001'
    TX_HK7002 = f'http://{__user}:{__pw}@{__tx_hk}:7002'
    TX_HK7003 = f'http://{__user}:{__pw}@{__tx_hk}:7003'
    TX_HK7004 = f'http://{__user}:{__pw}@{__tx_hk}:7004'
    TX_HK7005 = f'socks5://{__user}:{__pw}@{__tx_hk}:7005'
    TX_HK7009 = 'http://10.1.1.16:7009'

    """ 默认使用 """
    DEFAULT = TX_CN7009


class DESAdapter(HTTPAdapter):
    """ 绕过 JA3 指纹 """

    def __init__(self, *args, **kwargs):
        """
        A TransportAdapter that re-enables 3DES support in Requests.
        """
        origin_ciphers = (
            'ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:ECDH+HIGH:DH+HIGH:ECDH+3DES:DH+3DES:RSA'
            '+AESGCM :RSA+AES:RSA+HIGH:RSA+3DES '
        )
        ciphers = origin_ciphers.split(':')
        random.shuffle(ciphers)
        ciphers = ':'.join(ciphers)
        self.ciphers = ciphers + ':!aNULL:!eNULL:!MD5'
        super().__init__(*args, **kwargs)

    def init_poolmanager(self, *args, **kwargs):
        context = create_urllib3_context(ciphers=self.ciphers)
        kwargs['ssl_context'] = context
        return super(DESAdapter, self).init_poolmanager(*args, **kwargs)

    def proxy_manager_for(self, *args, **kwargs):
        context = create_urllib3_context(ciphers=self.ciphers)
        kwargs['ssl_context'] = context
        return super(DESAdapter, self).proxy_manager_for(*args, **kwargs)


class SubSession(Session, ProxyChannel):
    DEFAULT_RESPONSE_CODE = 500  # 默认响应状态码

    def __init__(self, max_retries=1):
        super().__init__()

        self.max_retries = max_retries

        self.verify = False
        self.hooks.update({
            "response": [
                hook_session
            ]
        })
        self.set_adapter()

    def set_adapter(self):
        """ 自定义配置 """
        retry = Retry(
            total=self.max_retries,  # 重试次数
            connect=0,  # 服务器连接错误重试次数
            status_forcelist=Retry.RETRY_AFTER_STATUS_CODES,  # 重试状态码
            allowed_methods=frozenset(['GET', 'POST']),  # 触发重试的方法
            raise_on_status=False,  # 重试完是否抛出异常
        )
        adapter = DESAdapter(max_retries=retry)
        self.mount('http://', adapter=adapter)
        self.mount('https://', adapter=adapter)

    def set_proxies(self, proxy=ProxyChannel.DEFAULT):
        """ 设置代理 默认使用内网负载均衡端口 """
        if proxy == self.CLOSE:
            self.verify = False
            self.proxies = {}
        else:
            self.verify = True
            self.proxies.update({
                'http': proxy,
                'https': proxy,
            })

    def get(self, url, timeout=10, **kwargs):
        time.sleep(random.randint(3, 5) / 10)  # 随机等待 0.3 - 0.5 秒
        try:
            response = super().get(url=url, timeout=timeout,
                                   hooks=dict(response=[hook_session]), **kwargs)
        except Exception as e:
            logger.error(f'SubSession.get Exception: {e}')

            response = Response()
            response.url = url
            response.status_code = self.DEFAULT_RESPONSE_CODE

            if hook_session:
                hook_session(response)

        return response

    def post(self, url, data=None, json=None, timeout=10, **kwargs):
        time.sleep(random.randint(3, 5) / 10)  # 随机等待 0.3 - 0.5 秒
        try:
            response = super().post(url=url, data=data, json=json, timeout=timeout,
                                    hooks=dict(response=[hook_session]), **kwargs)
        except Exception as e:
            logger.error(f'SubSession.post Exception: {e}')

            response = Response()
            response.url = url
            response.status_code = self.DEFAULT_RESPONSE_CODE

            if hook_session:
                hook_session(response)

        return response

    def __enter__(self):
        """ 支持上下文 with 语法，返回对象给 as 声明的变量"""
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        """ 支持上下文 with 语法，当 with 代码块执行完毕时执行 """
        self.close()

    def __del__(self):
        self.close()


if __name__ == '__main__':
    # 上下文语法
    with SubSession() as session:
        session.set_proxies(session.DEFAULT)

        logger.debug(session.proxies)
        resp = session.get('https://ja3er.com/json')
        logger.info(resp.json())

        s_time = time.time()
        resp = session.get('http://myip.ipip.net/')
        response_time = int((time.time() - s_time) * 1000)
        logger.info(f'{resp} | 响应：{response_time} ms | {resp.text}')

    # 对象
    session = SubSession()
    session.set_proxies(session.DEFAULT)

    logger.debug(session.proxies)
    resp = session.get('https://ja3er.com/json')
    logger.info(resp.json())

    with open(f'session_response_time.txt', 'a') as f:
        while True:
            s_time = time.time()
            resp = session.get('http://myip.ipip.net/')
            response_time = int((time.time() - s_time) * 1000)
            f.write(f'{resp} | 响应：{response_time} ms | {resp.text}\n')
            logger.info(f'{resp} | 响应：{response_time} ms | {resp.text}')
            time.sleep(1)
